package org.cybzacg.encryption.context.impl;

import org.cybzacg.encryption.context.EncryptionContext;
import org.cybzacg.encryption.config.EncryptionConfig;
import org.cybzacg.encryption.enums.EncryptionType;
import org.cybzacg.encryption.enums.EncryptionAlgorithm;
import org.cybzacg.encryption.enums.SecurityStrategyEnum;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.UUID;
import java.util.Objects;

/**
 * 默认加密上下文实现
 * 
 * @author encryption team
 * @version 1.0
 */
public class DefaultEncryptionContext implements EncryptionContext {
    
    private final String contextId;
    private final EncryptionConfig config;
    private final EncryptionType encryptionType;
    private final EncryptionAlgorithm algorithm;
    private final SecurityStrategyEnum securityStrategy;
    private final byte[] key;
    private final byte[] iv;
    private final byte[] publicKey;
    private final byte[] privateKey;
    private final Map<String, Object> attributes;
    private final LocalDateTime createTime;
    private boolean batchMode;
    private int batchSize;
    private boolean cacheEnabled;
    private long timeout;
    
    private DefaultEncryptionContext(Builder builder) {
        this.contextId = builder.contextId != null ? builder.contextId : UUID.randomUUID().toString();
        this.config = builder.config;
        this.encryptionType = builder.encryptionType != null ? builder.encryptionType : 
                             (config != null ? config.getEncryptionType() : EncryptionType.SYMMETRIC);
        this.algorithm = builder.algorithm != null ? builder.algorithm : 
                        (config != null ? config.getAlgorithm() : EncryptionAlgorithm.AES);
        this.securityStrategy = builder.securityStrategy != null ? builder.securityStrategy : 
                               (config != null ? config.getSecurityStrategy() : SecurityStrategyEnum.SAFE);
        this.key = builder.key;
        this.iv = builder.iv;
        this.publicKey = builder.publicKey;
        this.privateKey = builder.privateKey;
        this.attributes = new ConcurrentHashMap<>(builder.attributes);
        this.createTime = builder.createTime != null ? builder.createTime : LocalDateTime.now();
        this.batchMode = builder.batchMode;
        this.batchSize = builder.batchSize > 0 ? builder.batchSize : 
                        (config != null ? config.getBatchSize() : 100);
        this.cacheEnabled = builder.cacheEnabled;
        this.timeout = builder.timeout > 0 ? builder.timeout : 
                      (config != null ? config.getTimeout() : 15000);
    }
    
    @Override
    public EncryptionConfig getConfig() {
        return config;
    }
    
    @Override
    public EncryptionType getEncryptionType() {
        return encryptionType;
    }
    
    @Override
    public EncryptionAlgorithm getAlgorithm() {
        return algorithm;
    }
    
    @Override
    public SecurityStrategyEnum getSecurityStrategy() {
        return securityStrategy;
    }
    
    @Override
    public byte[] getKey() {
        return key != null ? key.clone() : null;
    }
    
    @Override
    public byte[] getIv() {
        return iv != null ? iv.clone() : null;
    }
    
    @Override
    public byte[] getPublicKey() {
        return publicKey != null ? publicKey.clone() : null;
    }
    
    @Override
    public byte[] getPrivateKey() {
        return privateKey != null ? privateKey.clone() : null;
    }
    
    @Override
    public Object getAttribute(String key) {
        return attributes.get(key);
    }
    
    @Override
    public void setAttribute(String key, Object value) {
        attributes.put(key, value);
    }
    
    @Override
    public Map<String, Object> getAttributes() {
        return new ConcurrentHashMap<>(attributes);
    }
    
    @Override
    public LocalDateTime getCreateTime() {
        return createTime;
    }
    
    @Override
    public String getContextId() {
        return contextId;
    }
    
    @Override
    public boolean isBatchMode() {
        return batchMode;
    }
    
    @Override
    public void setBatchMode(boolean batchMode) {
        this.batchMode = batchMode;
    }
    
    @Override
    public int getBatchSize() {
        return batchSize;
    }
    
    @Override
    public void setBatchSize(int batchSize) {
        this.batchSize = batchSize;
    }
    
    @Override
    public boolean isCacheEnabled() {
        return cacheEnabled;
    }
    
    @Override
    public void setCacheEnabled(boolean cacheEnabled) {
        this.cacheEnabled = cacheEnabled;
    }
    
    @Override
    public long getTimeout() {
        return timeout;
    }
    
    @Override
    public void setTimeout(long timeout) {
        this.timeout = timeout;
    }
    
    @Override
    public EncryptionContext copy() {
        return new Builder()
                .contextId(contextId)
                .config(config)
                .encryptionType(encryptionType)
                .algorithm(algorithm)
                .securityStrategy(securityStrategy)
                .key(key)
                .iv(iv)
                .publicKey(publicKey)
                .privateKey(privateKey)
                .attributes(new ConcurrentHashMap<>(attributes))
                .createTime(createTime)
                .batchMode(batchMode)
                .batchSize(batchSize)
                .cacheEnabled(cacheEnabled)
                .timeout(timeout)
                .build();
    }
    
    @Override
    public boolean isValid() {
        // 基本验证
        if (encryptionType == null || algorithm == null || securityStrategy == null) {
            return false;
        }
        
        // 密钥验证
        if (encryptionType.requiresKeyPair()) {
            // 非对称加密至少需要一个密钥（公钥用于加密，私钥用于解密）
            if (publicKey == null && privateKey == null) {
                return false;
            }
        } else if (key == null) {
            return false;
        }
        
        // 密钥长度验证
        if (key != null && !algorithm.isValidKeySize(key.length * 8)) {
            return false;
        }
        
        return true;
    }
    
    @Override
    public String getSummary() {
        return "DefaultEncryptionContext{" +
                "contextId='" + contextId + '\'' +
                ", encryptionType=" + encryptionType +
                ", algorithm=" + algorithm +
                ", securityStrategy=" + securityStrategy +
                ", hasKey=" + (key != null) +
                ", hasIv=" + (iv != null) +
                ", hasKeyPair=" + (publicKey != null && privateKey != null) +
                ", batchMode=" + batchMode +
                ", cacheEnabled=" + cacheEnabled +
                ", timeout=" + timeout +
                ", createTime=" + createTime +
                '}';
    }
    
    /**
     * 创建默认上下文
     * 
     * @return 默认上下文
     */
    public static DefaultEncryptionContext createDefault() {
        return new Builder().build();
    }
    
    /**
     * 从配置创建上下文
     * 
     * @param config 配置
     * @return 上下文
     */
    public static DefaultEncryptionContext fromConfig(EncryptionConfig config) {
        return new Builder()
                .config(config)
                .encryptionType(config.getEncryptionType())
                .algorithm(config.getAlgorithm())
                .securityStrategy(config.getSecurityStrategy())
                .cacheEnabled(config.isCacheEnabled())
                .timeout(config.getTimeout())
                .batchMode(config.isBatchMode())
                .batchSize(config.getBatchSize())
                .build();
    }
    
    /**
     * 建造者类
     */
    public static class Builder {
        private String contextId;
        private EncryptionConfig config;
        private EncryptionType encryptionType;
        private EncryptionAlgorithm algorithm;
        private SecurityStrategyEnum securityStrategy;
        private byte[] key;
        private byte[] iv;
        private byte[] publicKey;
        private byte[] privateKey;
        private Map<String, Object> attributes = new ConcurrentHashMap<>();
        private LocalDateTime createTime;
        private boolean batchMode = false;
        private int batchSize = 100;
        private boolean cacheEnabled = true;
        private long timeout = 15000;
        
        public Builder contextId(String contextId) {
            this.contextId = contextId;
            return this;
        }
        
        public Builder config(EncryptionConfig config) {
            this.config = config;
            return this;
        }
        
        public Builder encryptionType(EncryptionType encryptionType) {
            this.encryptionType = encryptionType;
            return this;
        }
        
        public Builder algorithm(EncryptionAlgorithm algorithm) {
            this.algorithm = algorithm;
            return this;
        }
        
        public Builder securityStrategy(SecurityStrategyEnum securityStrategy) {
            this.securityStrategy = securityStrategy;
            return this;
        }
        
        public Builder key(byte[] key) {
            this.key = key;
            return this;
        }
        
        public Builder iv(byte[] iv) {
            this.iv = iv;
            return this;
        }
        
        public Builder publicKey(byte[] publicKey) {
            this.publicKey = publicKey;
            return this;
        }
        
        public Builder privateKey(byte[] privateKey) {
            this.privateKey = privateKey;
            return this;
        }
        
        public Builder attribute(String key, Object value) {
            this.attributes.put(key, value);
            return this;
        }
        
        public Builder attributes(Map<String, Object> attributes) {
            this.attributes = new ConcurrentHashMap<>(attributes);
            return this;
        }
        
        public Builder createTime(LocalDateTime createTime) {
            this.createTime = createTime;
            return this;
        }
        
        public Builder batchMode(boolean batchMode) {
            this.batchMode = batchMode;
            return this;
        }
        
        public Builder batchSize(int batchSize) {
            this.batchSize = batchSize;
            return this;
        }
        
        public Builder cacheEnabled(boolean cacheEnabled) {
            this.cacheEnabled = cacheEnabled;
            return this;
        }
        
        public Builder timeout(long timeout) {
            this.timeout = timeout;
            return this;
        }
        
        public DefaultEncryptionContext build() {
            return new DefaultEncryptionContext(this);
        }
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        DefaultEncryptionContext that = (DefaultEncryptionContext) o;
        return batchMode == that.batchMode &&
                batchSize == that.batchSize &&
                cacheEnabled == that.cacheEnabled &&
                timeout == that.timeout &&
                Objects.equals(contextId, that.contextId) &&
                encryptionType == that.encryptionType &&
                algorithm == that.algorithm &&
                securityStrategy == that.securityStrategy &&
                Objects.equals(attributes, that.attributes);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(contextId, encryptionType, algorithm, securityStrategy, 
                          batchMode, batchSize, cacheEnabled, timeout, attributes);
    }
}
